package com.hyteria.render;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.newdawn.slick.Graphics;


public class Renderer {	
    private static ArrayList<Depth> renderables = new ArrayList<Depth>();  
    private static ArrayList<Integer> drawOrder = new ArrayList<Integer>(); //Contains the indices of the renderables to be drawn in the correct order, so they are drawn at the correct depth.
    private static ConcurrentLinkedQueue<Depth> depthChangeQueue = new ConcurrentLinkedQueue<Depth>(); //These are renderables whose depth's have changed and must be applied to the drawOrder. 
    private static int topDepth 	= 0;
    private static int bottomDepth 	= 0;
    
    public static void registerRenderable(Renderable r){ 
    	renderables.add(new Depth(r, 0, Depth.Type.New));
    	remapDrawOrder();
    	updateDepthBounds(0);
    }
    
    public static void removeRenderable(Renderable r){ 
    	int index = getRenderableIndex(r);
    	renderables.remove(r);
    	
    	for(int i = 0; i < drawOrder.size(); i++){
    		if(drawOrder.get(i) == index){
    			drawOrder.remove(i);
    			return;
    		}
    	}
    }
    
    public static ArrayList<Depth> getAll(){ return renderables; }
    public static List<Integer> getDrawOrder(){ return drawOrder; }
    
    public static int getRenderableDepth(Renderable rdr) throws Exception{
    	for(Depth d : getAll()){
    		if(d.renderable == rdr){
    			return d.depth;
    		}   		
    	}
    	
    	throw new Exception("Renderable does not exist.");
    }
    
    public static void setRenderableDepth(Renderable rdr, int depth){
    	depthChangeQueue.add(new Depth(rdr, depth, Depth.Type.Change));
    }
    
    public static int getRenderableIndex(Renderable rdr){
    	int index = 0;
    	for(Depth d : getAll()){
    		if(d.renderable == rdr){
    			return index;
    		}
    		
    		index++;
    	}
    	
    	return -1;
    }
    
    private static void updateDepthBounds(int depth){
    	if(depth < bottomDepth){
			bottomDepth = depth;
		}
		
		if(depth > topDepth){
			topDepth = depth;
		}
    }
    
    public static void update(){
    	Depth c;
    	boolean change = false;
    	while((c = depthChangeQueue.poll()) != null){
    		updateDepthBounds(c.depth);
    		
    		if(c.type == Depth.Type.New){
    			c.type = Depth.Type.Store;
    			renderables.add(c);
    		}
    		
    		if(c.type == Depth.Type.Change){
    			c.type = Depth.Type.Store;
    			for(Depth d : getAll()){
    				if(d.renderable == c.renderable){
    					d.depth = c.depth;
    				}
    			}
    		}
    		
    		change = true;
    	}
    	
    	if(change){
    		remapDrawOrder();
    	}
    }
    
    private static void remapDrawOrder(){
    	drawOrder.clear();
    	for(int d = bottomDepth; d <= topDepth; d++){
    		for(int i = 0; i < renderables.size(); i++){
    			Depth pair = renderables.get(i);
    			if(pair.depth == d){
    				drawOrder.add(i);
    			}
    		}
    	}
    }
    
    public static void render(Graphics g){
    	for(Integer i : drawOrder){
    		renderables.get(i).renderable.draw(g);
    	}
    }
    
    
    
    
}
